using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Utils : MonoBehaviour
{
    private static Texture2D _whiteTexture;

    public static Texture2D WhiteTexture
    {
        get
        {
            if (_whiteTexture == null)
            {
                _whiteTexture = new Texture2D(1, 1);
                _whiteTexture.SetPixel(0, 0, Color.white);
                _whiteTexture.Apply();
            }

            return _whiteTexture;
        }
    }

    public static void DrawScreenRect(Rect rect, Color color)
    {
        GUI.color = color;
        GUI.DrawTexture(rect, WhiteTexture);
        GUI.color = Color.white;
    }

    public static void DrawScreenRectBorder(Rect rect, float thickness, Color color)
    {
        // Top
        Utils.DrawScreenRect(new Rect(rect.xMin, rect.yMin, rect.width, thickness), color);
        // Left
        Utils.DrawScreenRect(new Rect(rect.xMin, rect.yMin, thickness, rect.height), color);
        // Right
        Utils.DrawScreenRect(new Rect(rect.xMax - thickness, rect.yMin, thickness, rect.height), color);
        // Bottom
        Utils.DrawScreenRect(new Rect(rect.xMin, rect.yMax - thickness, rect.width, thickness), color);
    }

    public static void DrawScreenRectByGL(Vector3 startPos, Vector3 endPos, Material material, Color color, float alpha)
    {
        GL.PushMatrix();
        if (!material)
            return;
        material.SetPass(0);
        GL.LoadPixelMatrix();

        GL.Begin(GL.QUADS);
        GL.Color(new Color(color.r, color.g, color.b, alpha));
        GL.Vertex3(startPos.x, startPos.y, 0);
        GL.Vertex3(endPos.x, startPos.y, 0);
        GL.Vertex3(endPos.x, endPos.y, 0);
        GL.Vertex3(startPos.x, endPos.y, 0);
        GL.End();

        GL.Begin(GL.LINES);
        GL.Color(color);
        GL.Vertex3(startPos.x, startPos.y, 0);
        GL.Vertex3(endPos.x, startPos.y, 0);
        GL.Vertex3(endPos.x, startPos.y, 0);
        GL.Vertex3(endPos.x, endPos.y, 0);
        GL.Vertex3(endPos.x, endPos.y, 0);
        GL.Vertex3(startPos.x, endPos.y, 0);
        GL.Vertex3(startPos.x, endPos.y, 0);
        GL.Vertex3(startPos.x, startPos.y, 0);
        GL.End();

        GL.PopMatrix();

    }

    public static Rect GetScreenRectOnGUI(Vector3 screenPosition1, Vector3 screenPosition2)
    {
        // Move origin from bottom left to top left
        screenPosition1.y = Screen.height - screenPosition1.y;
        screenPosition2.y = Screen.height - screenPosition2.y;
        // Calculate corners
        var topLeft = Vector3.Min(screenPosition1, screenPosition2);
        var bottomRight = Vector3.Max(screenPosition1, screenPosition2);
        // Create Rect
        return Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
    }

    public static Rect GetScreenRect(Vector3 screenPosition1, Vector3 screenPosition2)
    {
        // Calculate corners
        float minX = Mathf.Min(screenPosition1.x, screenPosition2.x);
        float maxX = Mathf.Max(screenPosition1.x, screenPosition2.x);
        float minY = Mathf.Min(screenPosition1.y, screenPosition2.y);
        float maxY = Mathf.Max(screenPosition1.y, screenPosition2.y);
        return Rect.MinMaxRect(minX, minY, maxX, maxY);

        //var topLeft = Vector3.Min(screenPosition1, screenPosition2);
        //var bottomRight = Vector3.Max(screenPosition1, screenPosition2);
        //// Create Rect
        //return Rect.MinMaxRect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
    }

    //create a bounding box (4 corners in order) from the start and end mouse position
    public static Vector2[] GetBoundingBox(Vector2 p1, Vector2 p2)
    {
        Vector2 newP1;
        Vector2 newP2;
        Vector2 newP3;
        Vector2 newP4;

        if (p1.x < p2.x) //if p1 is to the left of p2
        {
            if (p1.y > p2.y) // if p1 is above p2
            {
                newP1 = p1;
                newP2 = new Vector2(p2.x, p1.y);
                newP3 = new Vector2(p1.x, p2.y);
                newP4 = p2;
            }
            else //if p1 is below p2
            {
                newP1 = new Vector2(p1.x, p2.y);
                newP2 = p2;
                newP3 = p1;
                newP4 = new Vector2(p2.x, p1.y);
            }
        }
        else //if p1 is to the right of p2
        {
            if (p1.y > p2.y) // if p1 is above p2
            {
                newP1 = new Vector2(p2.x, p1.y);
                newP2 = p1;
                newP3 = p2;
                newP4 = new Vector2(p1.x, p2.y);
            }
            else //if p1 is below p2
            {
                newP1 = p2;
                newP2 = new Vector2(p1.x, p2.y);
                newP3 = new Vector2(p2.x, p1.y);
                newP4 = p1;
            }

        }

        Vector2[] corners = { newP1, newP2, newP3, newP4 };
        return corners;

    }

    public static bool CheckRectIntersect(Rect rect1, Rect rect2) 
    {
        return rect1.Overlaps(rect2);
    }

    public static Mesh GetWorldCombineMesh(GameObject go) 
    {
        SkinnedMeshRenderer[] skinRenderers = go.GetComponentsInChildren<SkinnedMeshRenderer>();
        MeshFilter[] meshFilters = go.GetComponentsInChildren<MeshFilter>();
        CombineInstance[] combineInstances = new CombineInstance[skinRenderers.Length + meshFilters.Length];

        int index = 0;
        for (int i = 0; i < skinRenderers.Length; i++)
        {
            var render = skinRenderers[i];
            var mesh = new Mesh();
            render.BakeMesh(mesh);
            combineInstances[index] = new CombineInstance
            {
                mesh = mesh,
                transform = render.gameObject.transform.localToWorldMatrix,
                subMeshIndex = 0
            };

            index++;
        }

        for (int i = 0; i < meshFilters.Length; i++)
        {
            var render = meshFilters[i];
            var temp = (render.sharedMesh != null) ? render.sharedMesh : render.mesh;
            var mesh = (Mesh)Instantiate(temp);
            combineInstances[index] = new CombineInstance
            {
                mesh = mesh,
                transform = render.gameObject.transform.localToWorldMatrix,
                subMeshIndex = 0
            };

            index++;
        }

        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(combineInstances, true, true);

        return combinedMesh;
    }

    public static Mesh GetWorldMesh(GameObject meshGO)
    {
        Mesh mesh = null;
        if (meshGO != null)
        {
            SkinnedMeshRenderer skinnedMeshRenderer = meshGO.GetComponent<SkinnedMeshRenderer>();
            if (skinnedMeshRenderer != null)
            {
                mesh = new Mesh();
                skinnedMeshRenderer.BakeMesh(mesh);
            }
            else
            {
                MeshFilter meshFilter = meshGO.GetComponent<MeshFilter>();
                mesh = (Mesh)Instantiate((meshFilter.sharedMesh != null) ? meshFilter.sharedMesh : meshFilter.mesh);
            }
        }

        if (meshGO == null || mesh == null) { return null; }

        CombineInstance combineInstance;
        combineInstance = new CombineInstance { mesh = mesh, transform = meshGO.transform.localToWorldMatrix, subMeshIndex = 0 };
        Mesh combinedMesh = new Mesh();
        combinedMesh.CombineMeshes(new CombineInstance[] { combineInstance }, true, true);
        return combinedMesh;
    }

    public static Vector3[] GetBoundsVertex(Bounds bounds)
    {
        Vector3 center = bounds.center;
        Vector3 size = bounds.size / 2;
        Vector3[] vertex = new Vector3[]
        {
                center - size,
                center + new Vector3(-size.x, size.y , size.z),
                center + new Vector3(size.x, -size.y , size.z),
                center + new Vector3(size.x, size.y , -size.z),
                center + new Vector3(-size.x, -size.y , size.z),
                center + new Vector3(size.x, -size.y , -size.z),
                center + new Vector3(-size.x, size.y , -size.z),
                center + size,
        };

        return vertex;
    }

    public static Rect GetScreenRect(Vector3[] worldVertex)
    {
        Rect rect = new Rect();
        float xMin = float.MaxValue, xMax = float.MinValue, yMin = float.MaxValue, yMax = float.MinValue;
        for (int i = 0; i < worldVertex.Length; i++)
        {
            Vector3 screenVertex = Camera.main.WorldToScreenPoint(worldVertex[i]);
            if (screenVertex.x < xMin)
            {
                xMin = screenVertex.x;
            }
            if (screenVertex.x > xMax)
            {
                xMax = screenVertex.x;
            }
            if (screenVertex.y < yMin)
            {
                yMin = screenVertex.y;
            }
            if (screenVertex.y > yMax)
            {
                yMax = screenVertex.y;
            }
        }

        rect = Rect.MinMaxRect(xMin, yMin, xMax, yMax);

        return rect;
    }
}

public static class MonoBehaviourExtensionMethods
{
    public static T AddAndGetComponent<T>(this GameObject gameObject) where T : Component
    {
        T component = gameObject.GetComponent<T>();
        if (component == null) 
        {
            component = gameObject.AddComponent<T>();
        }

        return component;
    }
}